/*
 * KKLayoutParser.cpp
 *
 *  Created on: 2013-9-15
 *      Author: wanghua
 */

#include "KKLayout.h"
#include "KKNode.h"
#include "XmlUtils.h"
#include <typeinfo>

USING_NS_CC;

KKLayout::KKLayout() {
	this->m_hAlign = KKAlignmentNone;
	this->m_vAlign = KKAlignmentNone;
	this->m_marginLeft = 0;
	this->m_marginRight = 0;
	this->m_marginTop = 0;
	this->m_marginBottom = 0;
	this->m_paddingLeft = 0;
	this->m_paddingRight = 0;
	this->m_paddingTop = 0;
	this->m_paddingBottom = 0;
	this->m_weight = 1;

}

KKLayout::~KKLayout() {
	// TODO Auto-generated destructor stub
}

void KKLayout::initFromXml(TiXmlElement* element) {
	const char* hAlign = element->Attribute("h-align");

	if (hAlign) {
		if (strcmp(hAlign, "right") == 0) {
			this->m_hAlign = KKAlignmentRight;
		} else if (strcmp(hAlign, "center") == 0) {
			this->m_hAlign = KKAlignmentCenter;
		} else if (strcmp(hAlign, "left") == 0) {
			this->m_hAlign = KKAlignmentLeft;
		}
	}

	const char* vAlign = element->Attribute("v-align");

	if (vAlign) {
		if (strcmp(vAlign, "top") == 0) {
			this->m_vAlign = KKAlignmentTop;
		} else if (strcmp(vAlign, "center") == 0) {
			this->m_vAlign = KKAlignmentCenter;
		} else if (strcmp(vAlign, "bottom") == 0) {
			this->m_vAlign = KKAlignmentBottom;
		}
	}

	this->m_marginLeft = XmlUtils::getIntAttr(element, "margin-left", 0);
	this->m_marginRight = XmlUtils::getIntAttr(element, "margin-right", 0);
	this->m_marginTop = XmlUtils::getIntAttr(element, "margin-top", 0);
	this->m_marginBottom = XmlUtils::getIntAttr(element, "margin-bottom", 0);

	this->m_paddingLeft = XmlUtils::getIntAttr(element, "padding-left", 0);
	this->m_paddingRight = XmlUtils::getIntAttr(element, "padding-right", 0);
	this->m_paddingTop = XmlUtils::getIntAttr(element, "padding-top", 0);
	this->m_paddingBottom = XmlUtils::getIntAttr(element, "padding-bottom", 0);

	this->m_weight = XmlUtils::getIntAttr(element, "weight", 1);
}

void KKLayout::measure(KKNode* node, KKNodeArray* children) {

	const float measurewidth = node->getWidth() - m_paddingLeft
			- m_paddingRight;
	const float measureHeight = node->getHeight() - m_paddingTop
			- m_paddingBottom;

	CCNode* child = NULL;

	KKNode* kknode = NULL;

	float marginLeft = 0;
	float marginRight = 0;
	float marginTop = 0;
	float marginBottom = 0;

	KKLayout* childLayout = NULL;
	KKNODE_FOREACH(children,kknode){
	child = dynamic_cast<CCNode*> (kknode);

	if (child) {

		childLayout = kknode->getLayout();

		if (childLayout) {
			marginLeft = childLayout->getMarginLeft();
			marginRight = childLayout->getMarginRight();
			marginTop = childLayout->getMarginTop();
			marginBottom = childLayout->getMarginBottom();
		} else {
			marginLeft = 0;
			marginRight = 0;
			marginTop = 0;
			marginBottom = 0;
		}

		measureChild(kknode, measurewidth - marginLeft - marginRight,
				measureHeight - marginTop - marginBottom);
	}

}
}

void KKLayout::measureChild(KKNode* child, const float& measureWidth,
		const float& measureHeight) {

	/*	CCLog("measure %s,w=%f,h=%f \n", child->getName().c_str(), measureWidth,
	 measureHeight);*/

	CCNode* ccchild = child->convertToCCNode();

	const float & w = child->m_width;
	const float & h = child->m_height;

	if (child->m_widthType == fill_parent) {
		child->m_width = measureWidth;

	} else if (child->m_widthType == wrap_content) {

		if (ccchild && ccchild->getContentSize().width > 0) {

			child->m_width = ccchild->getContentSize().width;

		}

	}

	if (child->m_heightType == fill_parent) {
		child->m_height = measureHeight;
	} else if (child->m_heightType == wrap_content) {

		if (ccchild && ccchild->getContentSize().height > 0) {

			child->m_height = ccchild->getContentSize().height;

		}

	}

	bool needResize = w != child->m_width || h != child->m_height;

	if (needResize) {
		child->resize(child->m_width, child->m_height);
	}

}

int KKLayout::getChildWidth(KKNode* node) {
	KKLayout* childLayout = node->getLayout();

	if (!childLayout) {
		return node->getWidth();
	}

	return node->getWidth() + childLayout->m_marginLeft
			+ childLayout->m_marginRight;
}

int KKLayout::getChildHeight(KKNode* node) {

	KKLayout* childLayout = node->getLayout();

	if (!childLayout) {
		return node->getHeight();
	}

	return node->getHeight() + childLayout->m_marginTop
			+ childLayout->m_marginBottom;
}

KKDictionary<string, KKLayoutParser*> KKLayoutParser::m_parsers;

KKLayout* KKLayoutParser::parseFromXml(TiXmlElement* element) {

	KKLayoutParser* parser = m_parsers.objectForKey(element->Attribute("name"));

	if (parser) {
		return parser->parse(element);
	}

	return NULL;
}

AbsLayout* AbsLayout::createFromXml(TiXmlElement* element) {
	AbsLayout* layout = new AbsLayout();

	layout->initFromXml(element);

	return layout;
}

void AbsLayout::layout(KKNode* node, KKNodeArray* children) {

}

RelativeLayout* RelativeLayout::createFromXml(TiXmlElement* element) {
	RelativeLayout* layout = new RelativeLayout();

	layout->initFromXml(element);

	return layout;
}

void RelativeLayout::layout(KKNode* node, KKNodeArray* children) {
	const float width = node->getWidth();
	const float height = node->getHeight();

	const float paddingLeft = m_paddingLeft;
	const float paddingRight = m_paddingRight;
	const float paddingTop = m_paddingTop;
	const float paddingBottom = m_paddingBottom;

	CCNode* child = NULL;

	KKNode* kknode = NULL;

	float x = 0;
	float y = 0;

	float anchorX = 0;
	float anchorY = 0;

	float childWidth = 0;
	float childHeight = 0;

	float marginLeft = 0;
	float marginRight = 0;
	float marginTop = 0;
	float marginBottom = 0;

	KKLayout* childLayout = NULL;
	KKNODE_FOREACH(children,kknode){
	child = dynamic_cast<CCNode*> (kknode);

	if (child) {

		childLayout = kknode->getLayout();

		if (childLayout) {

			x = child->getPositionX();
			y = child->getPositionY();

			anchorX = child->getAnchorPoint().x;
			anchorY = child->getAnchorPoint().y;

			childWidth = kknode->getWidth();
			childHeight = kknode->getHeight();

			marginLeft = childLayout->getMarginLeft();
			marginRight = childLayout->getMarginRight();
			marginTop = childLayout->getMarginTop();
			marginBottom = childLayout->getMarginBottom();

			if (childLayout->getHAlign() == KKAlignmentCenter) {
				x = width / 2 - (0.5 - anchorX) * childWidth + (marginLeft
						!= 0 ? marginLeft : -marginRight);
			} else if (childLayout->getHAlign() == KKAlignmentRight) {
				x = width - (1 - anchorX) * childWidth - marginRight
				- paddingRight;
			} else if (childLayout->getHAlign() == KKAlignmentLeft) {
				x = (anchorX) * childWidth + marginLeft + paddingLeft;
			} else {
				x = kknode->getX() + paddingLeft;
			}

			if (childLayout->getVAlign() == KKAlignmentCenter) {
				y = height / 2 - (0.5 - anchorY) * childHeight
				+ (marginBottom != 0 ? marginBottom : -marginTop);
			} else if (childLayout->getVAlign() == KKAlignmentTop) {
				y = height - (1 - anchorY) * childHeight - marginTop
				- paddingTop;
			} else if (childLayout->getVAlign() == KKAlignmentBottom) {
				y = (anchorY) * childHeight + marginBottom + paddingBottom;
			} else {
				y = kknode->getY() + paddingBottom;
			}

			kknode->m_x = x;
			kknode->m_y = y;
			child->setPosition(x, y);

			/*CCLog("%s x=%f,y=%f,w=%d,h=%f\n", kknode->getName().c_str(), x,
			 y, kknode->getWidth(), kknode->getScaledHeight());*/

		}
	}

}
}

LinearLayout* LinearLayout::createFromXml(TiXmlElement* element) {
	LinearLayout* layout = new LinearLayout();

	layout->initFromXml(element);

	const char* hGravity = element->Attribute("h-gravity");

	if (hGravity) {
		if (strcmp(hGravity, "right") == 0) {
			layout->m_hGravity = KKAlignmentRight;
		} else if (strcmp(hGravity, "center") == 0) {
			layout->m_hGravity = KKAlignmentCenter;
		} else if (strcmp(hGravity, "left") == 0) {
			layout->m_hGravity = KKAlignmentLeft;
		}
	}

	const char* vGravity = element->Attribute("v-gravity");

	if (vGravity) {
		if (strcmp(vGravity, "top") == 0) {
			layout->m_vGravity = KKAlignmentTop;
		} else if (strcmp(vGravity, "center") == 0) {
			layout->m_vGravity = KKAlignmentCenter;
		} else if (strcmp(vGravity, "bottom") == 0) {
			layout->m_vGravity = KKAlignmentBottom;
		}
	}

	layout->m_vgap = XmlUtils::getFloatAttr(element, "vgap", 0);

	layout->m_hgap = XmlUtils::getFloatAttr(element, "hgap", 0);

	layout->m_vertical = XmlUtils::getBoolAttr(element, "vertical", false);

	return layout;
}

void LinearLayout::measure(KKNode* node, KKNodeArray* children) {
	const float measurewidth = node->getWidth() - m_paddingLeft
			- m_paddingRight;
	const float measureHeight = node->getHeight() - m_paddingTop
			- m_paddingBottom;

	if (m_vertical) {
		measureVertical(node, children, measurewidth, measureHeight);
	} else {

	}
}

void LinearLayout::measureVertical(KKNode* node, KKNodeArray* children,
		const float & measurewidth, const float & measureHeight) {

	KKNode* kknode = NULL;

	float marginLeft = 0;
	float marginRight = 0;
	float marginTop = 0;
	float marginBottom = 0;
	float weight = 1;

	KKNodeArray* fillparentChildren = new KKNodeArray();
	fillparentChildren->retain();
	fillparentChildren->autorelease();

	float weightTotal = 0;
	float contentHeight = 0;

	KKLayout* childLayout = NULL;
	KKNODE_FOREACH(children,kknode){

	childLayout = kknode->getLayout();

	if (childLayout) {
		marginLeft = childLayout->getMarginLeft();
		marginRight = childLayout->getMarginRight();
		marginTop = childLayout->getMarginTop();
		marginBottom = childLayout->getMarginBottom();
		weight = childLayout->getWeight();
	} else {
		marginLeft = 0;
		marginRight = 0;
		marginTop = 0;
		marginBottom = 0;
		weight = 1;
	}

	measureChild(kknode, measurewidth - marginLeft - marginRight,
			measureHeight - marginTop - marginBottom);

	contentHeight += marginTop + marginBottom;

	if (kknode->m_heightType == fill_parent) {
		fillparentChildren->addObject(kknode);
		weightTotal += weight;
	} else {
		contentHeight += kknode->getHeight();
	}

}

	float fillHeight = measureHeight - contentHeight;

	if (fillHeight < 0) {
		fillHeight = 0;
	}

	if (weightTotal < 0) {
		weightTotal = 1;
	}

	KKNODE_FOREACH(fillparentChildren,kknode){

	childLayout = kknode->getLayout();

	if (childLayout) {
		weight = childLayout->getWeight();
	} else {
		weight = 1;
	}

	kknode ->resize(kknode->getWidth(), fillHeight * weight / weightTotal);
}

}

void LinearLayout::layout(KKNode* node, KKNodeArray* children) {
	const int& width = node->getWidth();
	const int& height = node->getHeight();

	if (m_vertical) {
		layoutVertical(node, children, width, height);
	} else {
		layoutHorizontal(node, children, width, height);
	}
}

void LinearLayout::layoutVertical(KKNode* node, KKNodeArray* children,
		const int & width, const int & height) {

	int left = 0;

	int top = height;

	int h = 0;

	if (m_vGravity == KKAlignmentCenter) {
		KK_NODE_CONTENT_HEIGHT(node, h);
		top = (height + h) / 2;

	} else if (m_vGravity == KKAlignmentTop) {
		top = height;
	} else {
		KK_NODE_CONTENT_HEIGHT(node, h);
		top = h;

	}

	int childWidth = 0;

	float x = 0;
	float y = 0;

	node->m_minX = 0;
	node->m_minY = 0;
	node->m_maxX = node->m_width;
	node->m_maxY = node->m_height;

	CCRect bounds;

	CCNode* child = NULL;
	KKNode* kknode = NULL;
	KKNODE_FOREACH(children,kknode){
	child = dynamic_cast<CCNode*> (kknode);
	if (child) {
		childWidth = getChildWidth(kknode);

		if (m_hGravity == KKAlignmentCenter) {
			left = (width - childWidth) / 2;
		} else if (m_hGravity == KKAlignmentRight) {
			left = width - childWidth;
		} else {
			left = 0;
		}

		x = left + child->getAnchorPoint().x * kknode->getWidth();

		y = top - (1 - child->getAnchorPoint().y) * kknode->getHeight();

		if (kknode->getLayout()) {
			x += kknode->getLayout()->getMarginLeft();
			y -= kknode->getLayout()->getMarginTop();
		}

		top -= getChildHeight(kknode);

		kknode->m_x = x;
		kknode->m_y = y;

		child->setPosition(x, y);

		bounds = child->boundingBox();

		node->m_minX = min(node->m_minX, bounds.getMinX());
		node->m_minY = min(node->m_minY, bounds.getMinY());

		node->m_maxX = max(node->m_maxX, bounds.getMaxX());
		node->m_maxY = max(node->m_maxY, bounds.getMaxY());
	}
}

}

void LinearLayout::layoutHorizontal(KKNode* node, KKNodeArray* children,
		const int & width, const int & height) {

	int left = 0;

	int top = height;

	int w = 0;

	if (m_hGravity == KKAlignmentCenter) {
		KK_NODE_CONTENT_WIDTH(node, w);
		left = (width - w) / 2;

	} else if (m_hGravity == KKAlignmentRight) {
		KK_NODE_CONTENT_WIDTH(node, w);
		left = width - w;
	}

	int childHeight = 0;
	float x = 0;
	float y = 0;

	node->m_minX = 0;
	node->m_minY = 0;
	node->m_maxX = node->m_width;
	node->m_maxY = node->m_height;

	CCRect bounds;

	CCNode* child = NULL;
	KKNode* kknode = NULL;
	KKNODE_FOREACH(children,kknode){
	child = dynamic_cast<CCNode*> (kknode);
	if (child) {
		childHeight = getChildHeight(kknode);

		if (m_vGravity == KKAlignmentCenter) {
			top = (height + childHeight) / 2;
		} else if (m_vGravity == KKAlignmentTop) {
			top = height;
		} else {
			top = childHeight;
		}

		x = left + child->getAnchorPoint().x * kknode->getWidth();

		y = top - (1 - child->getAnchorPoint().y) * kknode->getHeight();

		if (kknode->getLayout()) {
			x += kknode->getLayout()->getMarginLeft();
			y -= kknode->getLayout()->getMarginTop();
		}

		left += getChildWidth(kknode);

		child->setPosition(x, y);

		bounds = child->boundingBox();

		node->m_minX = min(node->m_minX, bounds.getMinX());
		node->m_minY = min(node->m_minY, bounds.getMinY());

		node->m_maxX = max(node->m_maxX, bounds.getMaxX());
		node->m_maxY = max(node->m_maxY, bounds.getMaxY());

	}
}

}

GridLayout* GridLayout::createFromXml(TiXmlElement* element) {
	GridLayout* layout = new GridLayout();

	layout->initFromXml(element);

	layout->m_cellWidth = XmlUtils::getFloatAttr(element, "cellw", 100);

	layout->m_cellHeight = XmlUtils::getFloatAttr(element, "cellh", 100);

	layout->m_vgap = XmlUtils::getFloatAttr(element, "vgap", 10);

	layout->m_hgap = XmlUtils::getFloatAttr(element, "hgap", 10);

	layout->m_column = XmlUtils::getFloatAttr(element, "numColumns", -1);

	const char* scaleType = element->Attribute("scale-type");

	if (scaleType) {
		if (strcmp(scaleType, "fitXY") == 0) {
			layout->m_scaleType = FITXY;
		} else if (strcmp(scaleType, "center_inside") == 0) {
			layout->m_scaleType = CENTER_INSIDE;
		} else if (strcmp(scaleType, "center_crop") == 0) {
			layout->m_scaleType = CENTER_CROP;
		}
	}

	const char* hGravity = element->Attribute("h-gravity");

	if (hGravity) {
		if (strcmp(hGravity, "right") == 0) {
			layout->m_hGravity = KKAlignmentRight;
		} else if (strcmp(hGravity, "center") == 0) {
			layout->m_hGravity = KKAlignmentCenter;
		} else if (strcmp(hGravity, "left") == 0) {
			layout->m_hGravity = KKAlignmentLeft;
		}
	}

	const char* vGravity = element->Attribute("v-gravity");

	if (vGravity) {
		if (strcmp(vGravity, "top") == 0) {
			layout->m_vGravity = KKAlignmentTop;
		} else if (strcmp(vGravity, "center") == 0) {
			layout->m_vGravity = KKAlignmentCenter;
		} else if (strcmp(vGravity, "bottom") == 0) {
			layout->m_vGravity = KKAlignmentBottom;
		}
	}

	return layout;
}

void GridLayout::layout(KKNode* node, KKNodeArray* children) {

	int column =
			m_column < 0 ?
					(node->getWidth() + m_hgap)
							/ (this->getCellWidth() + m_hgap) :
					m_column;

	if (column == 0) {
		column = 1;
	}

	KKNode* kknode = NULL;
	CCNode* child = NULL;

	float cellWidth = 0;
	float cellHeight = 0;

	if (m_scaleType == SCALE_NO) {
		KKNODE_FOREACH(children,kknode){
		if (kknode->m_width > cellWidth) {
			cellWidth = kknode->m_width;
		}

		if (kknode->m_height > cellHeight) {
			cellHeight = kknode->m_height;
		}
	}

} else {
	cellWidth = m_cellWidth;
	cellHeight = m_cellHeight;
}

	const int& width = node->getWidth();
	const int& height = node->getHeight();

	float left = 0;

	float top = height;

	if (m_hGravity == KKAlignmentCenter) {

		left = (width - (column * (cellWidth + m_hgap) - m_hgap)) / 2;

	} else if (m_hGravity == KKAlignmentRight) {

		left = width - (column * (cellWidth + m_hgap) - m_hgap);
	}

	int row = int(children->count()) / column + 1;

	if (m_vGravity == KKAlignmentCenter) {

		top = (height + (row * (cellHeight + m_vgap) - m_vgap)) / 2;

	} else if (m_vGravity == KKAlignmentBottom) {

		top = (row * (cellHeight + m_vgap) - m_vgap);
	}

	int childIndex = 0;

	int x = 0;
	int y = 0;

	node->m_minX = 0;
	node->m_minY = 0;
	node->m_maxX = node->m_width;
	node->m_maxY = node->m_height;

	float factor = 1;

	CCRect bounds;
	KKNODE_FOREACH(children,kknode){
	child = dynamic_cast<CCNode*> (kknode);

	if (child) {

		if (m_scaleType == FITXY) {
			child->setScaleX(cellWidth / child->getContentSize().width);
			child->setScaleY(cellHeight / child->getContentSize().height);
		} else if (m_scaleType == CENTER_INSIDE) {

			factor = min(
					cellWidth / child->getContentSize().width
					/ child->getScaleX(),
					cellHeight / child->getContentSize().height
					/ child->getScaleY());

			child->setScaleX(
					cellWidth * factor / child->getContentSize().width);
			child->setScaleY(
					cellHeight * factor / child->getContentSize().height);

		} else if (m_scaleType == CENTER_CROP) {

			factor = max(
					cellWidth / child->getContentSize().width
					/ child->getScaleX(),
					cellHeight / child->getContentSize().height
					/ child->getScaleY());

			child->setScaleX(
					cellWidth * factor / child->getContentSize().width);
			child->setScaleY(
					cellHeight * factor / child->getContentSize().height);
		}

		x = left + (childIndex % column) * (cellWidth + m_hgap) + cellWidth
		* child->getAnchorPoint().x;

		y = top - (childIndex / column) * (cellHeight + m_vgap)
		- cellHeight * (1 - child->getAnchorPoint().y);

		kknode->m_x = x;
		kknode->m_y = y;

		child->setPosition(ccp(x, y));

		node->m_ccNode->reorderChild(child, childIndex + 10);

		childIndex++;

		bounds = child->boundingBox();

		node->m_minX = min(node->m_minX, bounds.getMinX());
		node->m_minY = min(node->m_minY, bounds.getMinY());

		node->m_maxX = max(node->m_maxX, bounds.getMaxX());
		node->m_maxY = max(node->m_maxY, bounds.getMaxY());
	}

}
}
